﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Xunit;
using System.Web.Routing;
using System.Diagnostics;

namespace MvcPatch.Routing.Test
{
    public class PathBuilderTest
    {
        [Fact]
        public void ParseConstantPattern()
        {
            var pattern = "hello/world";
            var builder = new PathBuilder(pattern, null, null);

            Assert.Equal(1, builder.PartConstantMap.Length);
            Assert.Equal(1, builder.PartValues.Length);

            Assert.True(builder.PartConstantMap[0]);
            Assert.Equal(pattern, builder.PartValues[0]);
        }

        [Fact]
        public void ParsePatternStartWithPlaceHolder()
        {
            var pattern = "{hello}/world";
            var builder = new PathBuilder(pattern, null, null);

            Assert.Equal(2, builder.PartConstantMap.Length);
            Assert.Equal(2, builder.PartValues.Length);

            Assert.False(builder.PartConstantMap[0]);
            Assert.True(builder.PartConstantMap[1]);

            Assert.Equal("hello", builder.PartValues[0]);
            Assert.Equal("/world", builder.PartValues[1]);
        }

        [Fact]
        public void ParsePatternEndWithWildcardPlaceHolder()
        {
            var pattern = "hello/{*world}";
            var builder = new PathBuilder(pattern, null, null);

            Assert.Equal(2, builder.PartConstantMap.Length);
            Assert.Equal(2, builder.PartValues.Length);

            Assert.True(builder.PartConstantMap[0]);
            Assert.False(builder.PartConstantMap[1]);

            Assert.Equal("hello/", builder.PartValues[0]);
            Assert.Equal("world", builder.PartValues[1]);
        }

        [Fact]
        public void ParsePatternMixing()
        {
            var pattern = "hello/{world}/{next}/rest";
            var builder = new PathBuilder(pattern, null, null);

            Assert.Equal(5, builder.PartConstantMap.Length);
            Assert.Equal(5, builder.PartValues.Length);

            Assert.True(builder.PartConstantMap[0]);
            Assert.False(builder.PartConstantMap[1]);
            Assert.True(builder.PartConstantMap[2]);
            Assert.False(builder.PartConstantMap[3]);
            Assert.True(builder.PartConstantMap[4]);

            Assert.Equal("hello/", builder.PartValues[0]);
            Assert.Equal("world", builder.PartValues[1]);
            Assert.Equal("/", builder.PartValues[2]);
            Assert.Equal("next", builder.PartValues[3]);
            Assert.Equal("/rest", builder.PartValues[4]);
        }

        [Fact]
        public void BuildConstant()
        {
            var pattern = "hello/world";
            var builder = new PathBuilder(pattern, null, null);

            var token = builder.Build(new RouteValueDictionary());
            Assert.Equal(pattern, token);
        }

        [Fact]
        public void BuildSimplePath()
        {
            var pattern = "{controller}/{action}/{id}";
            var builder = new PathBuilder(
                pattern,
                new RouteValueDictionary(new { controller = "Home", action = "Index", id = "" }), null);

            Assert.Equal(
                "",
                builder.Build(new RouteValueDictionary(new { controller = "Home", action = "Index", id = "" })));
            Assert.Equal(
                "Account",
                builder.Build(new RouteValueDictionary(new { controller = "Account", action = "Index", id = "" })));
            Assert.Equal(
                "Home/About",
                builder.Build(new RouteValueDictionary(new { controller = "Home", action = "About", id = "" })));
            Assert.Equal(
                "Home/Index/5",
                builder.Build(new RouteValueDictionary(new { controller = "Home", action = "Index", id = "5" })));
            Assert.Equal(
                "Home/Index/123",
                builder.Build(new RouteValueDictionary(new { controller = "Home", id = "123" })));
        }

        [Fact]
        public void BuildWithDefaultsOnly()
        {
            var pattern = "account/{action}";
            var builder = new PathBuilder(pattern, new RouteValueDictionary(new { controller = "Account", action = "Index" }), null);

            Assert.Equal("account/login", builder.Build(new RouteValueDictionary(new { controller = "Account", action = "login" })));
        }

        [Fact]
        public void BuildFailed()
        {
            var pattern = "{controller}/{action}/{id}";
            var builder = new PathBuilder(
                pattern,
                new RouteValueDictionary(new { action = "Index", id = "" }), null);

            Assert.Null(builder.Build(new RouteValueDictionary(new { id = "123" })));
        }

        [Fact]
        public void BuildWithQueryString()
        {
            var pattern = "{controller}/{action}/{id}";
            var builder = new PathBuilder(
                pattern,
                new RouteValueDictionary(new { controller = "Home", action = "Index", id = "" }), null);

            Assert.Equal(
                "Home/About?q1=123&q2=456",
                builder.Build(new RouteValueDictionary(new { controller = "Home", action = "About", q1 = "123", q2 = "456" })));
        }

        // [Fact]
        public void Benchmark()
        {
            var currentValues = new RouteValueDictionary();
            var routeValues = new RouteValueDictionary(new { controller = "Account", action = "Home", id = "hello" });
            var defaults = new RouteValueDictionary(new { controller = "Account", action = "Index" });
            var constaints = new RouteValueDictionary();
            
            var pattern = "account/{action}";
            var parsedRoute = RouteParser.Parse(pattern);
            var tokenBuilder = new PathBuilder(pattern, defaults, constaints);

            int iteration = 1000 * 100;

            var watch = Stopwatch.StartNew();
            for (int i = 0; i < iteration; i++)
            {
                Assert.NotNull(parsedRoute.Bind(currentValues, routeValues, defaults, constaints));
            }
            Console.WriteLine(watch.Elapsed);

            watch.Reset();
            watch.Start();
            for (int i = 0; i < iteration; i++)
            {
                Assert.NotNull(tokenBuilder.Build(routeValues));
            }
            Console.WriteLine(watch.Elapsed);
        }

        [Fact]
        public void BuildMore()
        {
            var builder1 = new PathBuilder("{blog}", new RouteValueDictionary(new { controller = "Blog", action = "Index" }), null);
            Assert.Equal("jeffz", builder1.Build(new RouteValueDictionary(new { controller = "Blog", action = "Index", blog = "jeffz" })));

            var builder2 = new PathBuilder("{blog}/tag/{tag}", new RouteValueDictionary(new { controller = "Blog", action = "Tag" }), null);
            Assert.Equal("jeffz/tag/hello", builder2.Build(new RouteValueDictionary(new { controller = "Blog", action = "Tag", blog = "jeffz", tag = "hello" })));

            var builder3 = new PathBuilder("{blog}/archive/{year}/{month}.html", new RouteValueDictionary(new { controller = "Blog", action = "Archive" }), null);
            Assert.Equal("jeffz/archive/2009/2.html", builder3.Build(new RouteValueDictionary(new { controller = "Blog", action = "Archive", blog = "jeffz", year = 2009, month = 2 })));

            var builder4 = new PathBuilder("{blog}/archive/{*post}", new RouteValueDictionary(new { controller = "Blog", action = "Post" }), null);
            Assert.Equal("jeffz/archive/hello-world.html", builder4.Build(new RouteValueDictionary(new { controller = "Blog", action = "Post", blog = "jeffz", post = "hello-world.html" })));
        }
    }
}